/**
 * Copyright (c) Martijn Grootens (m.e.grootens@student.utwente.nl), 
 * University of Twente.
 * Created july 2012.
 *
 * This file is part of dbcleaner.
 * 
 * dbcleaner is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * dbcleaner is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with dbcleaner.  If not, see <http://www.gnu.org/licenses/>.
 */

package dbcleaner.core.dbentries;

import java.util.*;
import dbcleaner.core.tags.*;
import dbcleaner.core.bibfields.*;
import dbcleaner.core.parse.*;

/**
 * Representation of a general BibTeX reference.
 * The Entry class is not intended for direct use; instead use the 
 * subclasses Article, Book, etc.
 *
 * An Entry contains both obligatory and additional BibTeX Fields.
 * It has a list of Tags and can be printed directly to a BibTeX file.
 */
public class Entry {
    
    // Class constants; name of the BibTeX class and string for an
    // unlabeled BibTeX Entry.
    public static final String ENTRY_CLASS = "__entry__";
    private static final String UNLABELED   = "unlabeled";
    
    // Counter and ID. The static counter is initialized to -12, since
    // there are 12 classes present in the current DB; one instance of every
    // Java subclass is used for comparison with what's being read from file.
    private static int num_entries = -12;
    private int id;
    
    // The TeX label used for refering to the bibliography.
    private String label;     
    
    // One list for storing obligatory BibFields and one list for extra
    // fields. (Meant for distinguishing both types of fields. Another, 
    // possibly better, implementation would be to add a boolean "obligatory"
    // tot the BibField class.)
    protected List<BibField> fields_oblig;
    protected List<BibField> fields_extra;
    
    // List for storing the Tags.
    protected ArrayList<Tag> tags;
    
    // Boolean vars for the presence of Tags in the list if Tags and 
    // the presence of BibFields in the list of extra fields.
    private boolean has_tags;
    private boolean has_extra_fields;
    
    /**
     * Constructor for an EMPTY and general BibTeX entry;
     * note that one should not use this class directly.
     */
    public Entry () {
        
        // Set the id; also increment the counter of existing entries.
        this.id     = ++num_entries;
        
        // Initialize variables for a new and empty BibTeX Entry.
        this.label  = UNLABELED;
        
        this.has_tags = false;
        this.has_extra_fields = false;
        
        this.fields_oblig  = new ArrayList<BibField>();
        this.fields_extra  = new ArrayList<BibField>();
        
        this.tags   = new ArrayList<Tag>();
        
    }
    
    /**
     * Returns the ID of the instance.
     */
    public int get_id() { return id; }
    
    /**
     * Get the class name of this BibTeX reference.
     */
    public String get_class_name () {
        return ENTRY_CLASS;
    }
    
    /**
     * Set BibTeX entry's label
     */
    public void set_label (String label) {
        this.label = label;
    }
    
    /**
     * Get BibTeX entry's label
     */
    public String get_label () {
        if (label.equals(UNLABELED))
            return label + "_id_" + id;
        else
            return label;
    }
    
    /**
     * Return the contents of a field with name field_name.
     * If no such field_name is present, return the EMPTY statement
     * of the BibField class.
     */
    public String get_field_contents (String field_name) {
        
        // Search the obligatory fields.
        for (BibField b : fields_oblig)
            if (b.get_field_name().equals(field_name))
                return b.get_field_entry();
        
        // Search the extra fields.
        for (BibField b : fields_extra)
            if (b.get_field_name().equals(field_name))
                return b.get_field_entry();
        
        // If no such field was found, return EMPTY
        return BibField.EMPTY;
        
    }
    
    /**
     * Check if this Entry is incomplete; it is incomplete when
     * one of the fields contains BibField.EMPTY.
     */
    public boolean is_incomplete () {
        
        for (BibField b : fields_oblig)
            if (b.get_field_entry().equals(BibField.EMPTY))
                return true;
        
        return false;
        
    }
    
    
    /**
     * Add a BibField to the obligatory list.
     */
    public void add_oblig_field (BibField b) {
        fields_oblig.add(b);
    }
    
    /**
     * Add a BibField to the extra list, ensure this.has_extra_fields == true.
     */
    public void add_extra_field (BibField b) {
        
        fields_extra.add(b);
        has_extra_fields = true;
        
    }
	
	/**
	 * Return the List of extra fields
	 */
	public List<BibField> get_extra_fields () {
		return fields_extra;
	}
    
    
    /**
     * Append a new tag, ensure this.has_tags == true.
     */
    public void add_tag (Tag t) {
        
        this.tags.add(t);
        has_tags = true;
        
    }
    
    /**
     * Add a BibField to the Entry. Ensure that if it is an obligatory field,
     * the already present field contents are set to the newly added BibField's
     * field contents.
     */
    public void add_BibField (BibField to_add) {
        
        // Check if to_add is an obligatory field.
        boolean is_oblig = false;
        
        for (BibField b : fields_oblig) {
            if (b.get_field_name().equals(to_add.get_field_name())) {
                // If so, set it and break loop.
                is_oblig = true;
                fields_oblig.set(fields_oblig.indexOf(b), to_add);
                break;
            }
        }
        
        // If it was not obligatory, add the extra field.
        if (!is_oblig)
            add_extra_field(to_add);
        
    }
    
    /**
     * Not realy nice, but: search fields_extra for the BibField with
     * name "keywords" and return the keywords. If not present, return
     * null.
     */
    public String get_tags_from_extra () {
        
        for (BibField b : fields_extra)
            if (b.get_field_name().equals("keywords")) {
                return b.get_field_entry();
            }
        return null;
        
    }
    
    /**
     * Set the BibField called "keywords" to "old_keywords", so that
     * the list of Tags can replace the old keywords; the old keywords
     * will still be printed to the BibTeX file, to show the changes.
     */
    public void comment_tags_from_extra () {
        
        for (BibField b : fields_extra)
            if (b.get_field_name().equals("keywords")) 
                b.set_field_name("old_keywords");
        
    }
    
    
    /**
     * Print the Entry in BibTeX format, so that it can be used by TeX.
     */
    public String toString () {
        
        // Class name and TeX label.
        String ss = "ref_id = " + id + "\n@" + get_class_name() + "{" + get_label() + ",\n\n";
        
        // Obligatory fields.
        for (BibField b : fields_oblig)
            ss += b;
        
        // Extra fields.
        if (has_extra_fields) {
            ss += "\n";
            for (BibField b : fields_extra)
                ss += b;
        }
        
        // Print the tags (keywords).
        if (has_tags) {
            ss += "\n";
            BibField bb = new BibField("keywords","\n");
            for (Tag t : tags)
                bb.append_to_field_entry(t.toString());
            bb.append_to_field_entry("\t");
            ss += bb;
        }
        
        // Close the reference.
        ss += "\n}\n\n";
        
        return ss;
    }
    
}